package perform.broker;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import perform.annotations.ID;
import perform.criterion.Criterion;
import perform.criterion.Order;
import perform.mappings.ClassMappings;
import perform.mappings.ManyToManyMapping;
import perform.query.impl.CustomUpdateQuery;
import perform.query.impl.DeleteQuery;
import perform.query.impl.InsertQuery;
import perform.query.impl.M2MInsertQuery;
import perform.query.impl.RetrieveQuery;
import perform.query.impl.SelectQuery;
import perform.query.impl.UpdateQuery;
import perform.type.Type;

public class SimpleDatabaseBroker extends AbstractDatabaseBroker {
	
	public SimpleDatabaseBroker(){
		super();
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <S extends Type, T extends Type> void checkManyToOne(S type,
			Connection connection) throws Exception {
		Map<Field, String> map = ClassMappings.getManyToOnes(type.getClass());
		if(map != null){
			Object[] fields = map.keySet().toArray();
			for (int i = 0; i < fields.length; i++) {
				Field field = (Field)fields[i];
				T m2o = (T) field.get(type);
				if(m2o != null && !wrapper.isSaved(m2o)){
					m2o = save(m2o, connection);
					field.set(type, m2o);
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <S extends Type, T extends Type> void checkOneToOne(S type,
			Connection connection) throws Exception {
		Map<Field, String> map = ClassMappings.getOneToOnes(type.getClass());
		if(map != null){
			Object[] fields = map.keySet().toArray();
			for (int i = 0; i < fields.length; i++) {
				Field field = (Field)fields[i];
				T o2o = (T) field.get(type);
				if(o2o != null && !wrapper.isSaved(o2o)){
					o2o = save(o2o, connection);
					field.set(type, o2o);
				}
			}
		}
	}

	@Override
	protected <S extends Type> S insert(S type, Connection connection)
			throws Exception {
		InsertQuery query = new InsertQuery(type, connection);
		query.executeUpdate();
		Field idField = ClassMappings.getIDField(type.getClass());
		if(idField.getAnnotation(ID.class).autoIncrement()){
			query.setGeneratedKey(type);
		}
		query.close();
		wrapper.setSaved(type, true);
		return type;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <S extends Type, T extends Type> void checkOneToMany(S type,
			Connection connection) throws Exception {
		Map<Field, Field> map = ClassMappings.getOneToManies(type.getClass());
		if(map != null){
			Field[] fields = map.keySet().toArray(new Field[map.size()]);
			for(int i = 0; i < fields.length; i++){
				Object values = fields[i].get(type);
				if(values != null && values instanceof Type[]){
					T[] o2ms = (T[])values;
					for (int j = 0; j < o2ms.length; j++) {
						if(o2ms[j] != null && !wrapper.isSaved(o2ms[j])){
							o2ms[j] = save(o2ms[j], connection);
						}
						map.get(fields[i]).set(o2ms[j], type);
					}
					fields[i].set(type, o2ms);
				} else if(values != null){
					Object[] o2ms = ((List<T>)values).toArray();
					List<T> list = new ArrayList<T>(o2ms.length);
					for (int j = 0; j < o2ms.length; j++) {
						if(o2ms[j] != null && !wrapper.isSaved((T)o2ms[j])){
							list.add(save((T)o2ms[j], connection));
						} else {
							list.add((T)o2ms[j]);
						}
						map.get(fields[i]).set(o2ms[j], type);
					}
					fields[i].set(type, list);
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	protected <S extends Type, T extends Type> void checkManyToMany(S type,
			Connection connection) throws Exception {
		Map<Field, ManyToManyMapping> map = ClassMappings.getManyToManies(type.getClass());
		if(map != null){
			Object[] fields = map.keySet().toArray();
			for(int i = 0; i < fields.length; i++){
				Field field = (Field)fields[i];
				Object values = field.get(type);
				if(values != null && values instanceof Type[]){
					 T[] m2ms = (T[]) values;
					 for (int j = 0; j < m2ms.length; j++) {
						if(m2ms[j] != null && !wrapper.isSaved(m2ms[j])){
							m2ms[j] = save(m2ms[j], connection);
						}
						M2MInsertQuery query = new M2MInsertQuery(map.get(field), type, field, m2ms[j], connection);
						query.executeUpdate();
						query.close();
					}
					 field.set(type, m2ms);
				} else if(values != null){
					Object[] m2ms = ((List<T>)values).toArray();
					List<T> list = new ArrayList<T>(m2ms.length);
					for (int j = 0; j < m2ms.length; j++) {
						if(m2ms[j] != null && !wrapper.isSaved((T)m2ms[j])){
							list.add(save((T)m2ms[j], connection));
						} else {
							list.add((T)m2ms[j]);
						}
						M2MInsertQuery query = new M2MInsertQuery(map.get(field), type, field, list.get(j), connection);
						query.executeUpdate();
						query.close();
					}
					field.set(type, list);
				}
			}
		}
	}

	@Override
	public <T extends Type> void update(T type, Connection connection)
			throws Exception {
		UpdateQuery query = new UpdateQuery(type, connection);
		query.executeUpdate();
		query.close();
	}

	@Override
	public <T extends Type> void delete(T type, Connection connection)
			throws Exception {
		DeleteQuery query = new DeleteQuery(type, connection);
		query.executeUpdate();
		query.close();
	}

	@Override
	public <T extends Type> T get(T type, Connection connection)
			throws Exception {
		RetrieveQuery query = new RetrieveQuery(type, connection);
		type = processor.process(type, query.executeQuery());
		query.close();
		return type;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, Criterion criterion,
			Connection connection) throws Exception {
		SelectQuery query = new SelectQuery(clazz, criterion, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, Criterion criterion,
			Order order, Connection connection) throws Exception {
		SelectQuery query = new SelectQuery(clazz, criterion, order, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, Criterion criterion,
			Order order, int lowLimit, int highLimit, Connection connection)
			throws Exception {
		SelectQuery query = new SelectQuery(clazz, criterion, order, lowLimit, highLimit, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, String criteria,
			Connection connection) throws Exception {
		SelectQuery query = new SelectQuery(clazz, criteria, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, String criteria,
			Order order, Connection connection) throws Exception {
		SelectQuery query = new SelectQuery(clazz, criteria, order, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> get(Class<T> clazz, String criteria,
			Order order, int lowLimit, int highLimit, Connection connection)
			throws Exception {
		SelectQuery query = new SelectQuery(clazz, criteria, order, lowLimit, highLimit, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public <T extends Type> List<T> getCustom(Class<T> clazz, String from, Connection connection)
			throws Exception {
		SelectQuery query = new SelectQuery(from, clazz, connection);
		List<T> results = processor.process(clazz, query.executeQuery());
		query.close();
		return results;
	}

	@Override
	public void customUpdate(String query, Connection connection)
			throws Exception {
		CustomUpdateQuery query2 = new CustomUpdateQuery(query, connection);
		query2.executeUpdate();
		query2.close();
	}

}
